/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package com.yuecai.modules.yc_coupon.service;


import com.yuecai.exception.BadRequestException;
import com.yuecai.modules.yc_brand.domain.YcBrand;
import com.yuecai.modules.yc_brand.repository.YcBrandRepository;
import com.yuecai.modules.yc_category.domain.YcCategory;
import com.yuecai.modules.yc_category.repository.YcCategoryRepository;
import com.yuecai.modules.yc_coupon.domain.YcCoupon;
import com.yuecai.modules.yc_coupon.domain.vo.YcCouponVo;
import com.yuecai.modules.yc_coupon.repository.YcCouponRepository;
import com.yuecai.modules.yc_coupon.service.dto.YcCouponDto;
import com.yuecai.modules.yc_coupon.service.dto.YcCouponQueryCriteria;
import com.yuecai.modules.yc_coupon.service.mapstruct.YcCouponMapper;
import com.yuecai.modules.yc_order.domain.YcOrder;
import com.yuecai.modules.yc_order.repository.YcOrderRepository;
import com.yuecai.modules.yc_user_coupon.domain.YcUserCoupon;
import com.yuecai.modules.yc_user_coupon.repository.YcUserCouponRepository;
import com.yuecai.utils.FileUtil;
import com.yuecai.utils.QueryHelp;
import com.yuecai.utils.ValidationUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
* @website https://el-admin.vip
* @description 服务实现
* @author dm
* @date 2020-07-21
**/
@Service
@RequiredArgsConstructor
public class YcCouponService {

    private final YcCouponRepository ycCouponRepository;
    private final YcCouponMapper ycCouponMapper;
    private final YcUserCouponRepository ycUserCouponRepository;
    private final YcCategoryRepository ycCategoryRepository;
    private final YcBrandRepository ycBrandRepository;
    private final YcOrderRepository ycOrderRepository;

    
    public Map<String,Object> queryAll(YcCouponQueryCriteria criteria, Pageable pageable){
        Page<YcCoupon> page = ycCouponRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        List<YcCoupon> content = page.getContent();
        ArrayList<YcCouponVo> ycCouponVos = new ArrayList<>();
        content.forEach(item->{
            Integer couponType = item.getCouponType();
            String s = couponTypeDetals(couponType, item.getConditionsId());
            YcCouponVo ycCouponVo = new YcCouponVo();
            BeanUtils.copyProperties(item,ycCouponVo);
            ycCouponVo.setCouponTypeDetails(s);
            ycCouponVos.add(ycCouponVo);
        });
        Map<String,Object> map = new LinkedHashMap<>(2);
        map.put("content",page.getContent());
        map.put("totalElements",ycCouponVos);

        return map;
    }



    public String couponTypeDetals(Integer type,Long conditionsId){
//        1:全品类 2:限品类 3:限品牌
        if (type == 1){
            return "全品类";
        }else if (type == 2){
            Optional<YcCategory> byId = ycCategoryRepository.findById(Long.valueOf(conditionsId));
            if (byId.isPresent()){
                String name
                        = byId.get().getName();
                return name;
            }
        }else if (type == 3){
            Optional<YcBrand> byId = ycBrandRepository.findById(Long.valueOf(conditionsId));
            if (byId.isPresent()){
                String brandName
                        = byId.get().getBrandName();
                return brandName;
            }
        }
        return null;
    }

    
    public List<YcCouponDto> queryAll(YcCouponQueryCriteria criteria){
        return ycCouponMapper.toDto(ycCouponRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
    }

    
    @Transactional
    public YcCouponDto findById(Long id) {
        YcCoupon ycCoupon = ycCouponRepository.findById(id).orElseGet(YcCoupon::new);
        ValidationUtil.isNull(ycCoupon.getId(),"YcCoupon","id",id);
        return ycCouponMapper.toDto(ycCoupon);
    }

    
    @Transactional(rollbackFor = Exception.class)
    public YcCouponDto create(YcCoupon resources) {
        return ycCouponMapper.toDto(ycCouponRepository.save(resources));
    }

    
    @Transactional(rollbackFor = Exception.class)
    public void update(YcCoupon resources) {
        YcCoupon ycCoupon = ycCouponRepository.findById(resources.getId()).orElseGet(YcCoupon::new);
        ValidationUtil.isNull( ycCoupon.getId(),"YcCoupon","id",resources.getId());
        ycCoupon.copy(resources);
        ycCouponRepository.save(ycCoupon);
    }

    
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            ycCouponRepository.deleteById(id);
        }
    }

    
    public void download(List<YcCouponDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (YcCouponDto ycCoupon : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("优惠券类型 关联字典表 COUPON_TYPE  1:全品类 2:限品类 3:限制品牌", ycCoupon.getCouponType());
            map.put("条件ID coupon_type 为 2 3 时有值", ycCoupon.getConditionsId());
            map.put("条件的类型，0为金额，1为件数", ycCoupon.getConditionType());
            map.put("条件", ycCoupon.getConditions());
            map.put("优惠的类型，0为金额，1为折扣", ycCoupon.getDiscountType());
            map.put("优惠", ycCoupon.getDiscount());
            map.put("领取截止时间", ycCoupon.getExpireDate());
            map.put("领取后失效的天数", ycCoupon.getExpireDays());
            map.put("优惠券名称", ycCoupon.getCouponName());
            map.put("发行量，-1为无限制", ycCoupon.getCirculation());
            map.put("每人限制领取几张", ycCoupon.getAstrict());
            map.put("购物满多少可得 获取方式为购物赠券有值", ycCoupon.getShoppingForEnough());
            map.put("获取方式1注册赠券 2 物赠券赠送 3主动领取", ycCoupon.getWay());
            map.put("开始时间", ycCoupon.getStartDate());
            map.put("获取条件购买满多少钱 获取方式为2时有值", ycCoupon.getShoppingPrice());
            map.put("发行多少张", ycCoupon.getAllowance());
            map.put("备注", ycCoupon.getRemarks());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }



    /**
     *   CREATE TABLE `yc_coupon` (
     *   `coupon_type` tinyint(1) NOT NULL COMMENT '优惠券类型 关联字典表 COUPON_TYPE  1:全品类 2:限品类 3:限制品牌',
     *   `conditions_id` bigint DEFAULT NULL COMMENT '条件ID coupon_type 为 2 3 时有值',
     *   `condition_type` int NOT NULL COMMENT '条件的类型，0为金额，1为件数',
     *   `condition` decimal(12,2) NOT NULL COMMENT '条件',
     *   `discount_type` int NOT NULL COMMENT '优惠的类型，0为金额，1为折扣',
     *   `discount` decimal(12,2) NOT NULL COMMENT '优惠',
     *   `expire_date` timestamp NULL DEFAULT NULL COMMENT '领取截止时间',
     *   `expire_days` int DEFAULT NULL COMMENT '领取后失效的天数',
     *   `coupon_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '优惠券名称',
     *   `circulation` int NOT NULL COMMENT '发行量，-1为无限制',
     *   `astrict` int DEFAULT NULL COMMENT '每人限制领取几张',
     *   `shopping_for_enough` decimal(12,0) DEFAULT NULL COMMENT '购物满多少可得 获取方式为购物赠券有值',
     *   `way` int NOT NULL DEFAULT '1' COMMENT '获取方式 1注册赠券 2 购物赠券赠送 3主动领取 4后台发送',
     *   `start_date` timestamp NULL DEFAULT NULL COMMENT '开始时间',
     *   `shopping_price` decimal(12,2) DEFAULT NULL COMMENT '获取条件购买满多少钱 获取方式为2时有值',
     *   `allowance` int DEFAULT '0' COMMENT '发行多少张',
     *   `remarks` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
     *   PRIMARY KEY (`id`) USING BTREE
     * ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='优惠券';
     * @return
     */
    /**
     * 根据类型返回可以领取的优惠券，此处不包含关联用户的逻辑
     * 用户领取时间限制有两个，优先使用根据天数限制，天数不存在则按领取截止时间限制
     * @param way
     * @return
     */
    public List<YcCoupon> findYcCouponByType(Integer way,Long userId){
        List<YcCoupon> byWay = ycCouponRepository.findByWay(way);
        List<YcCoupon> list = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(byWay)){
            for (YcCoupon item : byWay) {
                Timestamp startDate = item.getStartDate();
                Timestamp now = new Timestamp(System.currentTimeMillis());
                boolean after = startDate.after(now);
                if (after){
                    //还未开始
                    continue;
                }
                Integer expireDays = item.getExpireDays();
                Timestamp expireDate = item.getExpireDate();
                if (expireDays==null && expireDate !=null && expireDate.before(now)){
                    //过期了，下一轮
                    continue;
                }
                Integer circulation = item.getCirculation();
                int allowance = item.getAllowance();
                boolean b = circulation > allowance;
                if (circulation!=-1&&!b){
                    //发出的数量达到了最大值
                    continue;
                }
                int i = ycUserCouponRepository.countByUserIdAndCouponId(userId, item.getId());
                Integer astrict = item.getAstrict();
                if (i >= astrict){
                    //该用户领取数量达到了顶峰
                    continue;
                }
                list.add(item);
            }
        }
        return list;
    }

    /**
     * 判断该用户能否领取优惠券
     * @param item
     * @param userId
     * @return
     */
    public boolean getYcCouponByCoupon(YcCoupon item,Long userId){
        Timestamp startDate = item.getStartDate();
        Timestamp now = new Timestamp(System.currentTimeMillis());
        boolean after = startDate.after(now);
        if (after){
            //还未开始
            throw new BadRequestException("优惠券领取时间还没到！");
        }
        Integer expireDays = item.getExpireDays();
        Timestamp expireDate = item.getExpireDate();
        boolean before = expireDate.before(now);
        if ((expireDays == null || "0".equals(expireDate))&& before){
            //过期了，下一轮
            throw new BadRequestException("优惠券已过期！");
        }
        Integer circulation = item.getCirculation();
        int allowance = item.getAllowance();
        boolean b = circulation > allowance;
        if (circulation!=-1&&!b){
            //发出的数量达到了最大值
            throw new BadRequestException("该优惠券已经被领取光了！");
        }
        int i = ycUserCouponRepository.countByUserIdAndCouponId(userId, item.getId());
        Integer astrict = item.getAstrict();
        if (i >= astrict){
            //该用户领取数量达到了顶峰
            throw new BadRequestException("您已经领取过该优惠券！");
        }
        return true;
    }


    /**
     * 用户主动领取
     * @param aLong
     * @return
     */
    public ResponseEntity<String> userInitiativeCoupone(Long aLong) {
        List<YcCoupon> list = findYcCouponByType(3, aLong);

        return ResponseEntity.ok("领取成功！");
    }

    @Transactional
    public ResponseEntity<String> getCouponById(Long id, Long aLong,Integer way) {
        Optional<YcCoupon> byId = ycCouponRepository.findById(id);
        if (byId.isPresent()){
            YcCoupon ycCoupon = byId.get();
            boolean ycCouponByCoupon = getYcCouponByCoupon(ycCoupon, aLong);
            if (ycCouponByCoupon){
                YcUserCoupon ycUserCoupon = saveYcUserCoupon(ycCoupon, aLong, way);
                ycUserCouponRepository.save(ycUserCoupon);
                int allowance = ycCoupon.getAllowance();
                ycCoupon.setAllowance(allowance+1);
                ycCouponRepository.save(ycCoupon);
                if (way==4){
                    return ResponseEntity.ok("发放成功！");
                }else {
                    return ResponseEntity.ok("领取成功！");
                }
            }
        }else {
            throw new BadRequestException("优惠券不存在！");
        }
        if (way==3){
            throw new BadRequestException("不符合领取条件！");
        }else{
            throw new BadRequestException("不符合发放条件！");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void register(Long id, Long pId) {
        try {
            List<YcCoupon> ycCouponByType = findYcCouponByType(1, id);
            if (CollectionUtils.isNotEmpty(ycCouponByType)){
                List<YcUserCoupon> list = new ArrayList<>();
                List<YcCoupon> items = new ArrayList<>();
                ycCouponByType.forEach(item->{
                    YcUserCoupon coupon = saveYcUserCoupon(item, id, 1);
                    int allowance = item.getAllowance();
                    item.setAllowance(allowance+1);
                    list.add(coupon);
                    items.add(item);
                });
                if (CollectionUtils.isNotEmpty(list)){
                    ycCouponRepository.saveAll(items);
                    ycUserCouponRepository.saveAll(list);
                }
            }
            //邀请赠券
            if (pId!=null){
                List<YcCoupon> ycCouponByType1 = findYcCouponByType(5, pId);
                if (CollectionUtils.isNotEmpty(ycCouponByType1)){
                    List<YcUserCoupon> list = new ArrayList<>();
                    List<YcCoupon> items = new ArrayList<>();
                    ycCouponByType1.forEach(item->{
                        YcUserCoupon coupon = saveYcUserCoupon(item, pId, 5);
                        int allowance = item.getAllowance();
                        item.setAllowance(allowance+1);
                        list.add(coupon);
                        items.add(item);
                    });
                    if (CollectionUtils.isNotEmpty(list)){
                        ycCouponRepository.saveAll(items);
                        ycUserCouponRepository.saveAll(list);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 生成用户优惠券表
     */
    public YcUserCoupon saveYcUserCoupon(YcCoupon coupon,Long userId,Integer way){
        YcUserCoupon ycUserCoupon = new YcUserCoupon();
        ycUserCoupon.setUserId(userId);
        Timestamp expireDate = null;
        if (coupon.getExpireDays()!=null){
            // 当前时间
            Calendar cal = Calendar.getInstance();
            // 日期的DATE减去10 就是往后推10 天 同理 +10 就是往后推十天
            cal.add(Calendar.DATE, coupon.getExpireDays());
            expireDate = new Timestamp(cal.getTimeInMillis());
        }else {
            expireDate = coupon.getExpireDate();
        }
        ycUserCoupon.setExpireDate(expireDate);
        ycUserCoupon.setCouponId(coupon.getId());
        ycUserCoupon.setCouponType(coupon.getCouponType());
        ycUserCoupon.setConditionsId(coupon.getConditionsId());
        ycUserCoupon.setConditionType(coupon.getConditionType());
        ycUserCoupon.setConditions(coupon.getConditions());
        ycUserCoupon.setDiscountType(coupon.getDiscountType());
        ycUserCoupon.setDiscount(coupon.getDiscount());
        ycUserCoupon.setRemarks(coupon.getRemarks());
        ycUserCoupon.setCouponWay(way);
        ycUserCoupon.setCreateTime(new Timestamp(System.currentTimeMillis()));
        ycUserCoupon.setIsUse(0);

        return ycUserCoupon;
    }


    /**
     * 购物赠券
     * @param payPrice
     * @param userId
     */
    @Async
    public void giveYcCoupon(BigDecimal payPrice, BigDecimal specialOffer, Long userId) {
        payPrice = payPrice.subtract(specialOffer);//去除特价商品价格
        List<YcCoupon> list = findYcCouponByType(2, userId);
        if(CollectionUtils.isNotEmpty(list)){
            List<YcUserCoupon> userCoupons = new ArrayList<>();
            List<YcCoupon> items = new ArrayList<>();
            for (YcCoupon item : list) {
                BigDecimal shoppingPrice = item.getShoppingPrice();
                if (shoppingPrice!=null&&shoppingPrice.compareTo(payPrice)>-1){
                    continue;
                }
                YcUserCoupon coupon = saveYcUserCoupon(item, userId, 2);
                userCoupons.add(coupon);
                int allowance = item.getAllowance();
                item.setAllowance(allowance+1);
                items.add(item);
            }
            if (CollectionUtils.isNotEmpty(userCoupons)){
                ycCouponRepository.saveAll(items);
                ycUserCouponRepository.saveAll(userCoupons);
            }
        }
    }


    @Async
    public void newUserFirstOrder(BigDecimal payPrice, BigDecimal specialOffer, Long userId, List<YcOrder> orders) {
        Set<String> collect = orders.stream().map(YcOrder::getOrderSn).collect(Collectors.toSet());
        List<YcOrder> allById = ycOrderRepository.findAllByOrderSnIsNotInAndUserId(collect,userId);
        if (CollectionUtils.isEmpty(allById)){
            payPrice = payPrice.subtract(specialOffer);//去除特价商品价格
            List<YcCoupon> list = findYcCouponByType(6, userId);
            if(CollectionUtils.isNotEmpty(list)){
                List<YcUserCoupon> userCoupons = new ArrayList<>();
                List<YcCoupon> items = new ArrayList<>();
                for (YcCoupon item : list) {
                    BigDecimal shoppingPrice = item.getShoppingPrice();
                    if (shoppingPrice!=null&&shoppingPrice.compareTo(payPrice)>-1){
                        continue;
                    }
                    YcUserCoupon coupon = saveYcUserCoupon(item, userId, 6);
                    userCoupons.add(coupon);
                    int allowance = item.getAllowance();
                    item.setAllowance(allowance+1);
                    items.add(item);
                }
                if (CollectionUtils.isNotEmpty(userCoupons)){
                    ycCouponRepository.saveAll(items);
                    ycUserCouponRepository.saveAll(userCoupons);
                }
            }
        }
    }

    /**
     * 根据类型返回可以领取的优惠券，此处不包含关联用户的逻辑
     * 用户领取时间限制有两个，优先使用根据天数限制，天数不存在则按领取截止时间限制
     * @param way
     * @return
     */
    public Set<YcCoupon> findYcCouponByType(Integer way,Set<Long> couponIds){
        List<YcCoupon> byWay = ycCouponRepository.findByWayAndIdIn(way,couponIds);
        Set<YcCoupon> list = new HashSet<>();
        if(CollectionUtils.isNotEmpty(byWay)){
            for (YcCoupon item : byWay) {
                Timestamp startDate = item.getStartDate();
                Timestamp now = new Timestamp(System.currentTimeMillis());
                boolean after = startDate.after(now);
                if (after){
                    //还未开始
                    continue;
                }
                Integer expireDays = item.getExpireDays();
                Timestamp expireDate = item.getExpireDate();
                if (expireDays==null && expireDate !=null && expireDate.before(now)){
                    //过期了，下一轮
                    continue;
                }
                Integer circulation = item.getCirculation();
                int allowance = item.getAllowance();
                boolean b = circulation > allowance;
                if (circulation!=-1&&!b){
                    //发出的数量达到了最大值
                    continue;
                }
                list.add(item);
            }
        }
        return list;
    }

    /**
     * 根据类型返回可以领取的优惠券，此处不包含关联用户的逻辑
     * 用户领取时间限制有两个，优先使用根据天数限制，天数不存在则按领取截止时间限制
     * @param way
     * @return
     */
    public List<YcCoupon> findYcCouponByType(Long userId,List<YcCoupon> byWay){
        List<YcCoupon> list = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(byWay)){
            for (YcCoupon item : byWay) {
                Timestamp startDate = item.getStartDate();
                Timestamp now = new Timestamp(System.currentTimeMillis());
                boolean after = startDate.after(now);
                if (after){
                    //还未开始
                    continue;
                }
                Integer expireDays = item.getExpireDays();
                Timestamp expireDate = item.getExpireDate();
                if (expireDays==null && expireDate !=null && expireDate.before(now)){
                    //过期了，下一轮
                    continue;
                }
                Integer circulation = item.getCirculation();
                int allowance = item.getAllowance();
                boolean b = circulation > allowance;
                if (circulation!=-1&&!b){
                    //发出的数量达到了最大值
                    continue;
                }
                int i = ycUserCouponRepository.countByUserIdAndCouponId(userId, item.getId());
                Integer astrict = item.getAstrict();
                if (i >= astrict){
                    //该用户领取数量达到了顶峰
                    continue;
                }
                list.add(item);
            }
        }
        return list;
    }
}